
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/statfs.h>
#include <linux/mount.h>
#include <linux/ext2_fs.h>
#include <linux/buffer_head.h>
#include <linux/pagemap.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>

#include "lab4fs.h"
#include "utils.h"

typedef struct ext2_dir_entry_2 ext2_dirent;

struct lab4fs_inode_info {
	struct inode vfs_inode;
};

////    utils funcs
void dump_inode_bmp(struct lab4fs_info *info);
void dump_blk_bmp(struct lab4fs_info *info);
int get_block_id(struct inode *inode)	// return the (block id) from disk-inode
{
	struct lab4fs_inode *pi = (struct lab4fs_inode *)(inode->u.generic_ip);
	printk("\n ---------  ino %ld, get blk %d\n", inode->i_ino,
	       pi->i_block[0]);
	return pi->i_block[0];
}

/////////////////////////

/////////////////////////////////////////////////////////////////
///////////                     file-operations

static int lab4fs_file_open(struct inode *inode, struct file *file);

static int
lab4fs_file_readdir(struct file *file, void *dirent, filldir_t filldir);

//static int lab4fs_file_release(struct inode *ino, struct file *file);

extern int ext2_readdir(struct file *filp, void *dirent, filldir_t filldir);

static int
lab4fs_file_readdir(struct file *file, void *dirent, filldir_t filldir);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
static struct super_block *lab4fs_get_sb(struct file_system_type *fs_type,
					 int flags, const char *dev_name,
					 void *data);
#else
static int lab4fs_get_sb(struct file_system_type *fs_type, int flags,
			 const char *dev_name, void *data,
			 struct vfsmount *mnt);
#endif

/////////////////////////////////////////////////////////////////
///////////                     inode-operations
static int
lab4fs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);

//static int lab4fs_create(struct inode *dir, struct dentry *dentry, int mode,
//                       struct nameidata *nd);

static struct dentry *lab4fs_inode_lookup(struct inode *parent_inode,
					  struct dentry *dentry,
					  struct nameidata *nameidata);

static int lab4fs_iop_setattr(struct dentry *dent, struct iattr *attr);

static struct inode *lab4fs_get_inode(struct super_block *sb,
				      struct dentry *dentry, int mode);
//static struct inode* lab4fs_get_inode( struct super_block* sb, int mode,  dev_t dev); 

// create a new disk-inode for reg-file(dentry), update root(dir) to add a dir-rec
static int lab4fs_iop_create(struct inode *dir,
			     struct dentry *dentry, int mode,
			     struct nameidata *nd);

static int lab4fs_iop_unlink(struct inode *dir, struct dentry *dentry);

static int lab4fs_iop_link(struct dentry *old_dentry, struct inode *dir,
			   struct dentry *dentry);

static struct buffer_head *lab4fs_find_entry(struct inode *dir,
					     const char *name, int namelen,
					     struct ext2_dir_entry_2 **res_dir);

static int lab4fs_super_write_inode(struct inode *inode, int wait);

//////////////////////////////////////////////////
///////                 address_space operations
static int lab4fs_writepage(struct page *page, struct writeback_control *wbc);

static int lab4fs_commit_write(struct file *file, struct page *page,
			       unsigned from, unsigned to);

static int lab4fs_readpage(struct file *file, struct page *page);

///////////////////////////////////////

static struct inode *lab4fs_root_inode;

static void lab4fs_kill_sb(struct super_block *super);
//static unsigned long get_sb_block(void **data);

/////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
static kmem_cache_t *lab4fs_inode_cachep;

#define LAB4FS_PGNUM	10
unsigned char glb_buffer[LAB4FS_PGNUM][PAGE_SIZE];	// tmp-buf for page-io

////////////////////////////////////////////////////////////////
static int glb_next_blk = 70;

static int look_next_disk_blk(void)	// only get next blk-id, not increase it
{
	if (glb_next_blk > BLK_COUNT) {
		printk("\t%s: Error!! next blk %d\n", __func__, glb_next_blk);
		return -1;
	}
	return glb_next_blk;
}

static int get_next_disk_blk(void)
{
	if (glb_next_blk > BLK_COUNT) {
		printk("\t%s: Error!! next blk %d\n", __func__, glb_next_blk);
		return -1;
	}

	return glb_next_blk++;
}

/*
	This func alloc "blkcnt" disk-blocks to inode, update blk-bmp, 

*/
static int alloc_disk_block(struct inode *inode, int blkcnt)
{
	struct super_block *sb = inode->i_sb;
	struct lab4fs_inode *li = inode->u.generic_ip;

	struct lab4fs_info *info = sb->s_fs_info;
	struct lab4fs_super *lsb = info->super;

	int i, cnt;
	unsigned char *bmp;

	printk("%s: --------> enter, sb %p, info %p, li %p, lsb %p\n",
	       __func__, sb, info, li, lsb);

	if (!sb || !info || !li || !lsb) {
		return -1;
	}

	bmp = info->blk_map;
	if (lsb->free_blocks < blkcnt) {
		printk("%s: free blk %d < %d, ret\n", __func__,
		       lsb->free_blocks, blkcnt);
		return -1;
	}
	/// start to search free-blks in bmp
	printk("\tassign free blk:  ");
	for (cnt = 0, i = 0; i < BLK_COUNT; i++) {
		if (bmp[i] == 0)	// find a free blk
		{
			li->i_block[cnt] = i;
			cnt++;
			bmp[i] = 1;
			lsb->free_blocks--;
			printk(" %d ", i);
		}
		if (cnt >= blkcnt)
			break;
	}
	li->i_blocks = cnt;	// update in-mem lab4fs-inode
	inode->i_blocks = cnt;

	printk(" \n");
	dump_blk_bmp(info);

	mark_inode_dirty(inode);
	mark_buffer_dirty(info->blkmap_bh);
	mark_buffer_dirty(info->sb_bh);
	return cnt;
}

/*
	Copy buf(size) to disk-blks of the inode
Return:
	num of bytes copied to disk
*/
static int copy_buf_to_disk(struct inode *inode, void *buf, int size)
{
	struct super_block *sb;
	struct lab4fs_inode *li;
	struct buffer_head *bh;
	int blk;

	dump_inode(inode);

	sb = inode->i_sb;
	li = inode->u.generic_ip;

//      struct lab4fs_info  *info = sb->s_fs_info;
//      struct lab4fs_super *lsb = info->super; 

	if (inode->i_blocks <= 0) {
		printk("\t%s:Erro: ind-blks = %ld\n", __func__,
		       inode->i_blocks);
		return -EIO;
	}

	blk = li->i_block[0];	// now we only use first data block       

	if (!(bh = sb_bread(sb, blk))) {
		printk("\t%s: fail to read blk %d\n", __func__, blk);
		return -EIO;
	}
	printk("\t%s: have read blk %d\n", __func__, blk);

	memcpy(bh->b_data, buf, size);

	mark_buffer_dirty(bh);	// now store the blk to disk
	brelse(bh);

	printk("\t%s:have stored %d bytes to disk blk %d\n", __func__, size,
	       blk);

	return size;
}

/*
	Load disk-blk of inode to buf(size)
*/
static int load_to_buf(struct inode *inode, void *buf, int size)	// load disk-blk to buf
{
	struct super_block *sb = inode->i_sb;
	struct lab4fs_inode *li = inode->u.generic_ip;

	struct buffer_head *bh;
	int blk;

	if (inode->i_blocks <= 0) {
		printk("\t%s:Erro: ind-blks = %ld\n", __func__,
		       inode->i_blocks);
		return -EIO;
	}

	blk = li->i_block[0];	// now we only use first data block       
	printk("\t%s: will read blk %d\n", __func__, blk);

	if (!(bh = sb_bread(sb, blk))) {
		printk("\t%s: fail to read blk %d\n", __func__, blk);
		return -EIO;
	}
	printk("\t%s: have read blk %d\n", __func__, blk);

	size = BLK_SIZE;
	memcpy(buf, bh->b_data, size);

	//mark_buffer_dirty(bh); // now store the blk to disk
	brelse(bh);

	printk("\t%s:have stored %d bytes to disk blk %d\n", __func__, size,
	       blk);
	return size;
}

//////////////////////////////////////////////////////////////////////
/////////////////////   helper func for inode-arrya opers

/* this func looks through the inode-map, find a first free inode, and 
returns the inode-num of the free inode	
Return:
	>0:  inode num;	<=0 :  fail
*/
static int alloc_disk_inode(struct super_block *sb)
{
	struct lab4fs_info *info = sb->s_fs_info;
	struct lab4fs_super *lab4sb = info->super;
	unsigned char *ibmp = info->ind_map;
	int i, k;

	if (lab4sb->free_inodes <= 0) {
		printk("\t%s: free-inods %d\n", __func__, lab4sb->free_inodes);
		return -1;
	}
	for (i = 0; i < INODE_COUNT; i++) {
		if (ibmp[i] == 0)	// find a free on-disk inode
		{
			k = i + LAB4FS_ROOT_INO;	//offset by the root's inode-num
			lab4sb->free_inodes--;
			ibmp[i] = 1;
			dump_inode_bmp(info);	// dump ind-bmp
			printk("\t%s: find free inode %d\n", __func__, k);

			mark_buffer_dirty(info->indmap_bh);
			mark_buffer_dirty(info->sb_bh);
			return k;
		}
	}

	printk("\t%s: failed to find free inode\n", __func__);
	lab4sb->free_inodes = 0;
	return -1;
}

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

static ssize_t lab4fs_file_write(struct file *file, const char __user * buf,
				 size_t count, loff_t * ppos)
{
	size_t ret;

	printk(" %s: ----------> enter\n", __func__);
	dump_file(file);

	ret = generic_file_write(file, buf, count, ppos);

	printk(" %s: gen-ret %d\n", __func__, ret);

	return ret;
}

// static int           phy_blk = 70;   // start from blk-70 on disk    

static int lab4fs_get_block(struct inode *inode, sector_t block,
			    struct buffer_head *bh_result, int create)
{
	static int cnt = 0;
	struct super_block *sb = inode->i_sb;
	struct lab4fs_info *sbinfo = sb->s_fs_info;
	struct buffer_head *sbh = sbinfo->sb_bh;
	int new_blk;
	int my_create = create;	//
	int phy_blk = look_next_disk_blk();	// start from blk-70 on disk    

	lock_kernel();

	printk("%s: ------> enter, %d, phy-blk=%d\n", __func__, cnt++, phy_blk);
	printk("\tblk=%ld, c=%d, my_c=%d\n", block, create, my_create);

	////////////////
	printk("\t1: my_create=%d\n", my_create);

	if (block < 0 || block >= BLK_COUNT)	// hard-code here. Assume free-blks < 800 )
	{
		printk("\t%s: Error!!! blk= %ld\n", __func__, block);
		unlock_kernel();
		return -EIO;
	}

	printk("\t my_create=%d, phy-blk=%d\n", my_create, phy_blk);
	if (my_create == 0) {
		//      if( block < 1 ) //current only handle one blk  (1024 && block % 64==0 )
		//new_blk = phy_blk;

		printk("\tc=%d, map blk %ld to phy_blk %d\n", my_create, block, phy_blk);	//new_blk );
		map_bh(bh_result, sb, phy_blk);	//

		unlock_kernel();
		return 0;
	}
	///////////
	new_blk = get_next_disk_blk();	//phy_blk++; // actually alloc a new blk on disk
	printk("\tc=%d, real map blk %ld to phy_blk %d\n", create, block,
	       new_blk);
	//lock_kernel();

	map_bh(bh_result, sb, new_blk);
	mark_inode_dirty(inode);
	mark_buffer_dirty(sbh);

	unlock_kernel();
	//////////////////////

	return 0;
}

static int lab4fs_prepare_write(struct file *file, struct page *page,
				unsigned from, unsigned to)
{
	int ret, ino;
	struct inode *inode;

	printk("\n%s: -------------->  enter, from %d to %d\n", __func__, from,
	       to);
	dump_file(file);

	////////                allocate some blks to the file, kept in inode
	inode = file->f_dentry->d_inode;
	dump_inode(inode);
	if (inode->i_blocks <= 0)	// alloc some blks to inode
	{
		alloc_disk_block(inode, PAGE_SIZE / BLK_SIZE);
	}
	// What about the code below?
	return 0;

	printk("%s: pg-idx %ld, from %d, to %d\n", __func__, page->index, from,
	       to);
	ret = block_prepare_write(page, from, to, lab4fs_get_block);
	/// it should ret 0 for success
	printk("<======== %s: blk_prep_write ret = %d \n", __func__, ret);
//      return 0; 
	return ret;
}

struct file_operations lab4fs_fops = {
	.llseek = generic_file_llseek,
	.read = generic_file_read,
	.write = lab4fs_file_write,	//generic_file_write,
	.mmap = generic_file_mmap,
	.sendfile = generic_file_sendfile,
	.readdir = lab4fs_file_readdir,
	.open = lab4fs_file_open
	    //.open           = generic_file_open,
};

struct address_space_operations lab4fs_aops = {
	.readpage = lab4fs_readpage,
	.writepage = lab4fs_writepage,
//      .sync_page      = block_sync_page,
	.prepare_write = lab4fs_prepare_write,
	.commit_write = lab4fs_commit_write,	//generic_commit_write,
//      .bmap           = lab4fs_bmap,
};

/*
static struct file_operations lab4fs_fops = {
      //open:lab4fs_file_open,
      //open: generic_file_open,
      //read:&do_sync_read,
      read:generic_file_read,
      //write:generic_file_write,
      //write:&do_sync_write,
      //aio_read: generic_file_aio_read,
      //aio_write: generic_file_aio_write,
      readdir:&lab4fs_file_readdir,
      //readdir:&ext2_readdir,
      //release:&lab4fs_file_release,
      //fsync:simple_sync_file
};*/

static struct file_system_type lab4fs = {
      name:"lab4fs",
      get_sb:lab4fs_get_sb,
      kill_sb:lab4fs_kill_sb,
      owner:THIS_MODULE
};

///////////////////////////////////////////////////////////////////////////
/////           inode_operations

static struct inode_operations lab4fs_iops = {
	.lookup = lab4fs_inode_lookup,
	.mknod = lab4fs_mknod,
	.create = lab4fs_iop_create,	/// 
	.link = lab4fs_iop_link,	//NULL, //simple_link,
	.unlink = lab4fs_iop_unlink,	//NULL, //simple_unlink,
//      rename:simple_rename
	.getattr = simple_getattr,	///lab4fs_iop_getattr,
	.setattr = lab4fs_iop_setattr,
/*
       int (*setattr) (struct dentry *, struct iattr *);
	int inode_setattr(struct inode * inode, struct iattr * attr) // at fs/attr.c

*/
};

//////  find first free inode in the inode table, return inode-num on disk
// root-inode num=1
int get_free_disk_inode(struct lab4fs_info *info)
{
	int i;

	if (info->super->free_inodes <= 0)
		return -1;

	for (i = 0; i < INODE_COUNT; i++) {
		if (info->ind_map[i] == 0)	// find a free inode
		{
			info->ind_map[i] = 1;
			info->super->free_inodes--;	// decrease free-inodes cnt
			mark_buffer_dirty(info->indmap_bh);
			dump_inode_bmp(info);
			return i + LAB4FS_ROOT_INO;	// root-inode starts from 1
		}
	}
	// cannot find free inodes, full
	info->super->free_inodes = 0;
	return -1;
//      info->super->free_inodes--;// decrease free-inodes cnt
//      return 4;
}

///  Insert a new dir-record(dentry)  into root-dir(dir)
static int insert_dentry_to_dir(struct inode *dir, struct dentry *dentry,
				int ino)
{
	struct super_block *sb;
	struct buffer_head *bh;
	int blk, padding;
	ext2_dirent *dire;
	__u16 i;

	printk("%s: ------------>  enter \n", __func__);

	sb = dir->i_sb;
	blk = get_block_id(dir);	///////////////      to be changed!! 

	if (sb == NULL) {
		printk(" %s:  ERROR sb is NULL", __func__);
		return -1;
	}

	if (!(bh = sb_bread(sb, blk))) {
		printk(" %s: fail to read blk %d\n", __func__, blk);
		return -1;
	}

	printk(" %s: step 1, will ins dire %s\n", __func__,
	       dentry->d_name.name);
	dire = (ext2_dirent *) (bh->b_data + dir->i_size);
	if (dire == NULL) {
		printk(" %s:  ERROR dire is NULL", __func__);
		goto out;
	}
	if (dentry == NULL) {
		printk(" %s:  ERROR dentry is NULL", __func__);
		goto out;
	}

	dire->inode = ino;
	dire->name_len = dentry->d_name.len;
	memcpy(dire->name, dentry->d_name.name, dire->name_len + 1);
	//printk(" %s:  after memcpy", __func__ );
	dire->file_type = 1;	// 0: unknow, 1: regular file. 2:dir

	i = (__u16) (sizeof(int) + 2 + 1 + 1 + dire->name_len + 1);
	padding = 4 - i % 4;
	dire->rec_len = i + padding;

	/////////// update dir-inode
	dir->i_size += dire->rec_len;
	dir->i_mtime = CURRENT_TIME;

	printk(" %s:  step 2, dire-len=%d, new dir-size=%lld\n", __func__,
	       dire->rec_len, dir->i_size);

	mark_buffer_dirty(bh);	// update the block for dir-inode
      out:
	brelse(bh);

	printk(" %s: step 3, finished \n", __func__);
	return 0;
}

/// inode_op
int lab4fs_iop_setattr(struct dentry *dent, struct iattr *attr)
{
	int i;
	struct inode *inode;

	printk("%s: ---------> enter...\n", __func__);
	dump_dentry(dent);

	inode = dent->d_inode;
	dump_inode(inode);

	printk("\t%s: step 1\n", __func__);
	i = inode_setattr(inode, attr);	// change attr of inode
	printk("\t%s: step 2\n", __func__);
	//i = mark_inode_dirty(inode);
	lab4fs_super_write_inode(inode, 0);

	printk("\t%s: step 3, ret %d\n", __func__, i);
	return i;

}

/*
	Let dentry be a link to old_dentry
*/
static int lab4fs_iop_link(struct dentry *old_dentry, struct inode *dir,
			   struct dentry *dentry)
{
	struct inode *inode;
	int ino;

	mydbg("%s: ------------------> enter\n", __func__);

	dump_dentry(old_dentry);
	dump_dentry(dentry);

	inode = old_dentry->d_inode;
	ino = inode->i_ino;
	dump_inode(inode);

	// update the dir's dirent records      
	insert_dentry_to_dir(dir, dentry, inode->i_ino);

	return simple_link(old_dentry, dir, dentry);
}

/// unlink dentry from dir
static int lab4fs_iop_unlink(struct inode *dir, struct dentry *dentry)
{
	int i;
	struct inode *inode;
	struct buffer_head *bh;
	ext2_dirent *dire;

	printk("%s: ----------> enter\n", __func__);
	dump_inode(dir);
	dump_dentry(dentry);

	inode = dentry->d_inode;	// inode of the dentry
	/// inode->i_nlink--;   // decrease the nlink

	//// update the dir-record for this inode
	bh = lab4fs_find_entry(dir, dentry->d_name.name, dentry->d_name.len,
			       &dire);
	if (!bh || dire->inode != inode->i_ino) {
		mydbg("\t%s: Error!! fail to find dentry in dir\n", __func__);
		return -ENOENT;
	}

	if (inode->i_nlink < 1)	// the file has been deleted
	{
		mydbg("\t%s: Error!! ind-nlink=%d\n", __func__, inode->i_nlink);
		inode->i_nlink = 1;
	}

	dire->inode = 0;	// mark this dir-ent as invalid, so it is not counted in "ls"
	mark_buffer_dirty(bh);

	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
	mark_inode_dirty(dir);

	inode->i_nlink--;
	inode->i_ctime = dir->i_ctime;
	mark_inode_dirty(inode);

	brelse(bh);		// reflect the dir-change to disk
	i = 0;

	printk("%s: <============  ret %d\n", __func__, i);

	return i;
}

//// create a new disk-inode for reg-file(dentry), update root(dir) to add a dir-rec
static int lab4fs_iop_create(struct inode *dir, struct dentry *dentry,
			     int mode, struct nameidata *nd)
{
	struct inode *inode;
	struct super_block *sb = dir->i_sb;
//      struct lab4fs_info *pinfo = (struct lab4fs_info*)sb->s_fs_info ;
//      unsigned long ino;

	printk(" ---  %s: --------> enter \n", __func__);
	//inode = new_inode(sb);// this calls super_op->alloc() to get an in-memory inode
	//inode = iget(sb, 100); //just a dummy ino here
	inode = lab4fs_get_inode(sb, dentry, mode | S_IFREG | DEF_ACCESS);	// create a reg-file
	if (!inode) {
		printk("- %s: Error!!!!  fail to get new_inode\n", __func__);
		return -ENOSPC;
	}
	/*
	   ///get a disk-inode
	   ino = get_free_disk_inode(pinfo);
	   printk(" %s: get a ");
	   if( ino <= LAB4FS_ROOT_INO )
	   {
	   printk(" Error: %s: fail get disk inode %d\n", __func__, ino );
	   return -ENOSPC;
	   }    */

	inode->i_blksize = 1 << BLK_SHIFT;
	inode->i_blkbits = BLK_SHIFT;

	inode->i_op = &lab4fs_iops;
	inode->i_fop = &lab4fs_fops;
	inode->i_mapping->a_ops = &lab4fs_aops;

	inode->i_sb = sb;
	///////////////////////////
	printk("iop_create, insert_inode_hash\n");
	insert_inode_hash(inode);
	mark_inode_dirty(inode);
	printk("iop_create,after insert_inode_hash\n");

	//// update the dir-rec in dir-block    
	insert_dentry_to_dir(dir, dentry, inode->i_ino);
	if (dir == NULL || dentry == NULL) {
		printk("iop_create: dir or dentry is null\n");
		return -1;
	}

	d_instantiate(dentry, inode);	// add an dentry-inode bind       
	printk("iop_create: after_d_instantiate\n");

	return 0;

}

/* super block operations */
//static char file_buf[PAGE_SIZE] = "Hello World\n";
//static int file_size = 12;

/* address_space_operations */
static int lab4fs_writepage(struct page *page, struct writeback_control *wbc)
{
	void *page_addr = kmap(page);
	int ino, i;

	printk("%s:  -----------> enter\n", __func__);

	dump_page(page);
	ino = page->mapping->host->i_ino;
	i = ino % LAB4FS_PGNUM;

	printk("%s: cp pg to buf %d\n", __func__, i);
	memcpy(glb_buffer[i], page_addr, PAGE_SIZE);

	printk("%s: step 1\n", __func__);
	ClearPageDirty(page);
	printk("%s: step 2\n", __func__);
	if (PageLocked(page)) {

		printk("%s: step 3\n", __func__);
		unlock_page(page);
	}

	printk("%s: step 4\n", __func__);
	kunmap(page);
	printk("%s: step 5\n", __func__);
	return 0;
}

////    address_space oper:  readpage
static int lab4fs_readpage(struct file *file, struct page *page)
{
	struct inode *inode = file->f_dentry->d_inode;
	void *page_addr = kmap(page);
	int ret;

	printk("%s: --------------> enter\n", __func__);
	dump_file(file);
	dump_inode(inode);
	dump_page(page);

	ret = load_to_buf(inode, page_addr, PAGE_SIZE);	// load disk-blk to buf

	printk("%s: have load size %d to pg\n", __func__, ret);

	SetPageUptodate(page);
	if (PageLocked(page))
		unlock_page(page);

	kunmap(page);

	return 0;
}

//unmount lab4fs filesystem

static void lab4fs_kill_sb(struct super_block *super)
{
	kill_block_super(super);
}

/* from rkfs
 * modified from generic_commit_write. generic_commit_write calls the
 * block device layer to write set up buffer heads for I/O.
 */
static int lab4fs_commit_write(struct file *file, struct page *page,
			       unsigned int from, unsigned int to)
{
	struct inode *inode;
	void *page_addr;
	int pos, i;

	printk("%s: -------------> enter, from %d, to %d\n", __func__, from,
	       to);

	inode = page->mapping->host;
	page_addr = kmap(page);
	//loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
	pos = (0 << PAGE_CACHE_SHIFT) + to;	// assume page-index == 0

	dump_file(file);
	dump_inode(inode);
	dump_page(page);

	i = inode->i_ino % LAB4FS_PGNUM;

	/// now copy content from page-> buf -> disk
	// printk(" %s: now copy  page -> buf[%d], len %d\n", __func__, i, pos);

	//////// As a test, copy to buf
	memcpy(glb_buffer[i], page_addr, PAGE_SIZE);	//pos );

	/////// now will copy to disk
	printk(" %s: now copy  page -> disk[%d],len %d\n", __func__, i, pos);
	copy_buf_to_disk(inode, page_addr, pos);
	///static int   copy_buf_to_disk(struct inode* inode, void *buf, int size)

	ClearPageDirty(page);
	SetPageUptodate(page);
	if (PageLocked(page)) {
		printk(" %s: step 4\n", __func__);
		//unlock_page(page); // dont unlock here. Generic_commit will unlock the page 
	}
	printk(" %s: step 5\n", __func__);
	kunmap(page);

	printk(" %s: step 6\n", __func__);
	//// update file size
	if (pos > inode->i_size) {

		inode->i_size = pos;
		//i_size_write(inode, pos);
		printk(" %s: step 6.5, new-size %d\n", __func__, pos);
		mark_inode_dirty(inode);
	}

	printk(" %s: step 7\n", __func__);
	return 0;

}

/*
 allocates an in-disk inode, and use this in-disk inode's ino-num to create an in-mem inode
Input:
	assign_ino:   if > 0, assign this ino to new inode. Otherwise use my algorithm to get an ino
*/
static struct inode *lab4fs_get_inode(struct super_block *sb,
				      struct dentry *dentry, int mode)
			//, int assign_ino )
{
	struct inode *inode;
	int ino;
	printk(" %s: sb %p, mode %x, dname %s\n", __func__, sb, mode,
	       dentry->d_name.name);
	ino = alloc_disk_inode(sb);
	if (ino <= 0)		// fail
	{
		printk("%s:  Fail to get a in-disk inode\n", __func__);
		return NULL;
	}

	printk("\t%s: before iget()\n", __func__);
	inode = iget(sb, ino);	// sb will actually alloc an in-mem inode
	printk("\t%s: after iget()\n", __func__);

	/// now init the inode
	inode->i_ino = ino;
	inode->i_mode = mode;
	inode->i_uid = current->fsuid;
	inode->i_gid = current->fsgid;
	inode->i_blksize = BLK_SIZE;
	inode->i_blocks = 0;
	inode->i_size = 0;
	inode->i_nlink = 1;	/// init 
	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
	printk("%s: have init inode %d\n", __func__, ino);

	switch (mode & S_IFMT) {
	case S_IFREG:
		inode->i_op = &lab4fs_iops;
		inode->i_fop = &lab4fs_fops;
		break;
	case S_IFDIR:
		inode->i_op = &lab4fs_iops;
		inode->i_fop = &lab4fs_fops;
		//Let dir-inode's i_nlink starts from 2: '.' and '..'
		inode->i_nlink++;
		break;
	case S_IFLNK:
		printk(" %s:  LNK not supported yet!!!! \n", __func__);
		inode->i_op = &page_symlink_inode_operations;
		break;
	default:
		printk("\t%s: Error: mode %x unknow\n", __func__, mode);
		/// init_special_inode(inode, mode, dev);
		break;
	}

	return inode;
}

static struct lab4fs_inode *lab4fs_get_disk_inode(struct super_block *sb,
						  int ino,
						  struct buffer_head **bh)
{
	// start at beginning
	int blk = 3;		// inode-table starts from blk 3;
	int offset;

	if (!(*bh = sb_bread(sb, blk)))	// read a block  starting at 0
	{
		printk("lab4fs: get_inode unable to read superblock\n");
	}
	offset = (ino - 1) * sizeof(struct lab4fs_inode);
	printk(" %s: have read inode %d from bdev\n", __func__, ino);

	return (struct lab4fs_inode *)((*bh)->b_data + offset);

}

/////   This func seems no use ???  No longer used
// we use ext2_dir_entry2 struct
/*static struct ext2_dir_entry_2 *lab4fs_get_dir_entry(struct super_block *sb,
						     struct inode *inode,
						     int offset, int size)
{
	// start at beginning
	int blk = get_block_id(inode);

	// start at correct 
	struct buffer_head *bh2;
	int bufoffset = inode->i_blocks * BLK_SIZE + offset;
	struct ext2_dir_entry_2 *dire = NULL;
	printk("lab4fs_get_dir_entry, ino_num %lu\n", inode->i_ino);
	printk(" %s: inod->iblocks = %d\n", __func__, (int)inode->i_blocks);
	printk(" %s: bufoffset = %d\n", __func__, bufoffset);
	printk
	    ("lab4fs_get_dir_entry before sb_bread #of blocks is %ld, blocksize =%ld\n",
	     inode->i_blocks, sb->s_blocksize);
	// problem here...
	//if ( !(bh= sb_bread(sb,bufoffset)) )
	//if ( !(bh2= sb_bread(sb, 0)) )

	//if ( !(bh2= sb_bread(sb,inode->i_blocks)) )
	if (!(bh2 = sb_bread(sb, blk))) {
		printk("%s: unable to read superblock\n", __func__);

	}
	printk("lab4fs_get_dir_entry after sb_bread\n");
	//offset = sizeof( struct lab4fs_super);

	//dire = (struct ext2_dir_entry_2*) (((char *)bh->b_data) + size);
	//dire = (struct ext2_dir_entry_2*) (((char *)bh2->b_data) + bufoffset);
	dire = (struct ext2_dir_entry_2 *)(((char *)bh2->b_data) + offset);	//bufoffset 
	printk(" %s: name %s, rec_len:%d, inode =%d\n", __func__, dire->name,
	       dire->rec_len, dire->inode);
	// dire = NULL;
	return dire;

}*/

//////// will reclaim in-disk inode/blocks for this inode
static void lab4fs_super_delete_inode(struct inode *inode)
{
	struct super_block *sb;
	struct lab4fs_info *info;
	struct lab4fs_super *lsb;
	struct lab4fs_inode *di;
	struct buffer_head *bh;
	int blk, i, ino;

	mydbg("%s: -----------> enter\n", __func__);

	dump_inode(inode);

	sb = inode->i_sb;
	info = sb->s_fs_info;	// ind-bmp, blk-bmp, buffer-header
	lsb = info->super;	// ind/blk counters

	ino = inode->i_ino;

	//// free the in-disk inode
	inode->i_size = 0;
	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;

	lock_kernel();

//      inode->i_nlink = 0;
	mark_inode_dirty(inode);

	blk = (ino - LAB4FS_ROOT_INO) / LAB4FS_INODE_PER_BLK;
	// the disk blk that store this inode
	bh = sb_bread(sb, blk);
	i = (ino - LAB4FS_ROOT_INO) % LAB4FS_INODE_PER_BLK;	//
	mydbg("\t%s: free ind %d, at blk %d off %d\n", __func__, ino, blk, i);

	di = (struct lab4fs_inode *)bh->b_data + i;	// di now is in-disk inode
	di->i_size = 0;
	di->i_blocks = 0;
	mark_buffer_dirty(bh);
	brelse(bh);

	/// update inode-bmp
	mydbg("\t: free ind %d\n", ino);
	info->ind_map[ino - LAB4FS_ROOT_INO] = 0;	// mark the ind as free
	lsb->free_inodes++;
	dump_inode_bmp(info);

	/// update block-bmp
	di = (struct lab4fs_inode *)inode->u.generic_ip;
	// di now is the memcp of in-disk inode
	mydbg("\t%s: free %lu blks: ", __func__, inode->i_blocks);
	for (i = 0; i < inode->i_blocks; i++) {
		blk = di->i_block[i];
		info->blk_map[blk] = 0;
		lsb->free_blocks++;
		mydbg(" %d ", blk);

	}
	inode->i_blocks = 0;
	dump_blk_bmp(info);

	mark_buffer_dirty(info->sb_bh);
	mark_buffer_dirty(info->blkmap_bh);
	mark_buffer_dirty(info->indmap_bh);

	////////////
	unlock_kernel();
	clear_inode(inode);

	mydbg("%s: <============ exit\n", __func__);
}

// super_operations
static void lab4fs_super_read_inode(struct inode *inode)
{
	struct buffer_head *bh;
	struct lab4fs_inode *raw_inode;

	// read inode from device.
	int ino = 1;		// the root-inode = 1 
	ino = inode->i_ino;
	printk(" %s: going to read inode %d\n", __func__, ino);

	raw_inode = lab4fs_get_disk_inode(inode->i_sb, ino, &bh);
	printk(" %s: raw_ind mode= 0x %x, mtime %d\n", __func__,
	       raw_inode->i_mode, raw_inode->i_mtime);

	inode->i_mtime.tv_sec = raw_inode->i_mtime;
	inode->i_ctime.tv_sec = raw_inode->i_ctime;
	inode->i_atime.tv_sec = raw_inode->i_atime;

	inode->i_mode = raw_inode->i_mode;
	inode->i_uid = raw_inode->i_uid;
	inode->i_gid = raw_inode->i_gid;

	//inode->i_blocks
	inode->i_nlink = raw_inode->i_links_count;

	inode->i_blocks = raw_inode->i_blocks;
	inode->i_size = raw_inode->i_size;

	/////////// hard-code here ...............
	printk("\tset ind->blkbits to %d\n", BLK_SHIFT);
	inode->i_blkbits = BLK_SHIFT;
	inode->i_blksize = BLK_SIZE;
	////////////

	//////// record lab4fs_inode info in inode
	inode->u.generic_ip = raw_inode;
	/////////

	if (S_ISREG(inode->i_mode)) {
		inode->i_op = &lab4fs_iops;
		inode->i_mapping->a_ops = &lab4fs_aops;
	}

	inode->i_op = &lab4fs_iops;	// newly added 
	inode->i_mapping->a_ops = &lab4fs_aops;
	//      brelse(bh);
	printk("LAB4FS: %s return \n", __func__);
}

// from rkfs
static int lab4fs_super_write_inode(struct inode *inode, int wait)
{
	struct buffer_head *bh;
	struct lab4fs_inode *inmem_li;
	struct lab4fs_inode *di;
	int i, ino;

	printk("%s: ------------> enter \n", __func__);
	dump_inode(inode);

	ino = inode->i_ino;

	inmem_li = (struct lab4fs_inode *)(inode->u.generic_ip);

	printk("\t%s: going to write inode %d\n", __func__, ino);

	di = lab4fs_get_disk_inode(inode->i_sb, ino, &bh);
	printk(" %s: raw_ind mode= 0x %x, mtime %d\n", __func__, di->i_mode,
	       di->i_mtime);
	printk(" %s: inmem-ind mode %x, mtime %d\n", __func__, inode->i_mode,
	       inode->i_mtime);

	//di->i_ino = inode->i_ino;
	di->i_mode = inode->i_mode;
	di->i_uid = inode->i_uid;
	di->i_gid = inode->i_gid;
	di->i_links_count = inode->i_nlink;

	di->i_atime = inode->i_atime.tv_sec;
	di->i_mtime = inode->i_mtime.tv_sec;
	di->i_ctime = inode->i_ctime.tv_sec;

	//inode->i_blocks
	di->i_blocks = inode->i_blocks;
	di->i_size = inode->i_size;

	for (i = 0; i < inode->i_blocks; i++) {
		di->i_block[i] = inmem_li->i_block[i];
	}

	//// save
	mark_buffer_dirty(bh);
	brelse(bh);
	return 0;

}

/////////////////////////////////
static int
lab4fs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
	///// lab4fs_get_inode( struct super_block* sb, int ino, struct buffer_head *bh)
	struct inode *inode = lab4fs_get_inode(dir->i_sb, dentry, mode | S_IFREG | DEF_ACCESS);	/// 
	int error = -ENOSPC;

	printk(" %s: -----> get inode %lu\n", __func__, inode->i_ino);
	if (inode) {
		d_instantiate(dentry, inode);
		dget(dentry);	/* Extra count - pin the dentry in core */
		error = 0;
		dir->i_mtime = dir->i_ctime = CURRENT_TIME;
		// insert this dentry into parent-dir
		insert_dentry_to_dir(dir, dentry, inode->i_ino);
		///dir->i_size += 0x20;  /* bogus small size for each dir entry */
		mark_inode_dirty(inode);
	}

	return error;
}

/*static int lab4fs_create(struct inode *dir, struct dentry *dentry, int mode,
			 struct nameidata *nd)
{
	return lab4fs_mknod(dir, dentry, mode | S_IFREG, 0);
}*/

//static char filename[] = "hello.txt";
//static int filename_len = sizeof (filename) - 1;

static inline int lab4fs_namecmp(int len, const char *name, const char *buffer)
{

	if (len >= 255 || buffer[len])
		return 0;
//      printk(" %s: will memcmp: %s, %s, %d\n", __func__, name, buffer, len );
	return !memcmp(name, buffer, len);
}

static struct buffer_head *lab4fs_find_entry(struct inode *dir,
					     const char *name, int namelen,
					     struct ext2_dir_entry_2 **res_dir)
{
	unsigned long block, offset;
	struct buffer_head *bh;
	struct ext2_dir_entry_2 *de;

	int blk = get_block_id(dir);

	*res_dir = NULL;
	if (namelen > 255)
		return NULL;
	bh = NULL;
	block = offset = 0;
	printk("%s: dir->blk %d, want to find %s\n", __func__, blk, name);
	printk("\tnamelen %d, dir-isize %llu\n", namelen, dir->i_size);
	// dir->i_blocks, name, namelen, dir->i_size, dir->i_blocks );
	//if ( dir->i_size !=2 );
	// dir->i_size  
	//for( i=0; i<2; i++ ){ ///while (offset < 100 ) 
	while (offset < dir->i_size) {
		if (!bh) {
			bh = sb_bread(dir->i_sb, blk);
			//bh= sb_bread(dir->i_sb,dir->i_blocks);
			if (!bh) {
				printk("%s: cannot read bh\n", __func__);	//block++;
				return NULL;
			}
		}
		de = (struct ext2_dir_entry_2 *)(bh->b_data + offset);
		printk(" %s: get de->name %s, len %d, de-reclen %d, inode %d\n",
		       __func__, de->name, de->name_len, de->rec_len,
		       de->inode);
		offset += de->rec_len;
		if (de->inode && lab4fs_namecmp(de->name_len, name, de->name)) {
			//printk(" %s: memcmp succeed. \n", __func__ );
			*res_dir = de;
			return bh;
		}
		/* if (offset < bh->b_size)
		   continue; */
		/*brelse(bh);
		   bh = NULL;
		   offset = 0;
		   block++; */
	}
	brelse(bh);
	return NULL;
}

///// inode_operations
static struct dentry *lab4fs_inode_lookup(struct inode *parent_inode,
					  struct dentry *dentry,
					  struct nameidata *nameidata)
{

	//printk ("LAB4fs: parent_inode size =  %d, \n");

	struct ext2_dir_entry_2 *de;
	struct buffer_head *bh;
	struct inode *file_inode = NULL;
	printk("lab4fs:   lab4fs_inode_lookup\n");

	if (parent_inode == NULL)
		printk("LAB4fs: parent_inode is NULL!\n");
	if (dentry == NULL)
		printk("LAB4fs: dentry is NULL!\n");
	if (nameidata == NULL)
		printk("LAB4fs: nameidata is NULL!\n");

	printk("lookup: dentry->d_name.name: %s\n", dentry->d_name.name);
	printk("lookup: dentry->d_name.len: %d\n", dentry->d_name.len);
	if (dentry->d_fsdata != NULL)
		printk("lookup: dentry->d_fsdata: %p\n", dentry->d_fsdata);
	//printk("lookup: dentry->d_inode size: %d\n", dentry->d_inode->i_size);
	//printk("lookup: dentry->d_inode no: %d\n", dentry->d_inode->i_ino);

	/* struct super_block *sb = parent_inode->i_sb;
	   struct buffer_head *bh;
	   if ( !(bh= sb_bread(sb,parent_inode->i_blocks)) )//  
	   {
	   printk (" %s: unable to read par_ind.block %d\n", __func__, parent_inode->i_blocks );
	   goto out;
	   }
	   printk(" %s: have read par_ind.blk %d\n", __func__, parent_inode->i_blocks );
	 */
	//goto out;
	if (dentry->d_inode == NULL) {
		printk(" %s: lookup:  d_inode is NULL\n", __func__);
		//      goto out;
	}
	///// now look over all entries in the block for parent_inode(this is a dir) 
	//to look for a fname matching dentry 
	bh = lab4fs_find_entry(parent_inode, dentry->d_name.name,
			       dentry->d_name.len, &de);
	if (bh) {
		unsigned long ino = de->inode;
		printk(" %s: will get inode %lu\n", __func__, ino);

		file_inode = iget(parent_inode->i_sb, ino);	//dentry->d_inode->i_ino);
		printk(" %s: got ino %lu, mode %x\n", __func__,
		       file_inode->i_ino, file_inode->i_mode);
		if (!file_inode)
			return ERR_PTR(-EACCES);
		// file_inode->i_size = dentry->d_inode->i_size;
		// dentry->d_inode = file_inode; //
		file_inode->i_mode |= DEF_ACCESS;	///= S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
		file_inode->i_fop = &lab4fs_fops;
	}
	// TODO: set dentry_ops for dentry

	d_add(dentry, file_inode);

	//out:
	return NULL;
}

/*
 * File Operations
 */

static int lab4fs_file_open(struct inode *inode, struct file *file)
{
	printk(" %s: -------------> enter\n", __func__);
	dump_file(file);
	//dump_inode(inode);
	//      (int) file->f_ra.ra_pages, file_size);
	if (inode == NULL)
		printk("file_open: inode == NULL\n");
	file->f_ra.ra_pages = 0;	/* No read-ahead */
	return generic_file_open(inode, file);
}

/*static int lab4fs_file_release(struct inode *ino, struct file *file)
{
	struct dentry *dentry;
	dentry = file->f_dentry;
	return 0;
}*/

static inline unsigned long dir_pages(struct inode *inode)
{
	return (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
}

static inline unsigned ext2_chunk_size(struct inode *inode)
{
	return inode->i_sb->s_blocksize;
}

static inline ext2_dirent *ext2_next_entry(ext2_dirent * p)
{
	return (ext2_dirent *) ((char *)p + le16_to_cpu(p->rec_len));
}

/*static unsigned ext2_last_byte(struct inode *inode, unsigned long page_nr)
{
	unsigned last_byte = inode->i_size;

	last_byte -= page_nr << PAGE_CACHE_SHIFT;
	if (last_byte > PAGE_CACHE_SIZE)
		last_byte = PAGE_CACHE_SIZE;
	return last_byte;
}*/

static inline void lab4fs_put_page(struct page *page)
{
	kunmap(page);
	page_cache_release(page);
}

/*static struct page *lab4fs_get_page(struct inode *dir, unsigned long n)
{
	struct address_space *mapping = dir->i_mapping;
	struct page *page = read_cache_page(mapping, n,
					    (filler_t *) mapping->a_ops->
					    readpage, NULL);
	if (!IS_ERR(page)) {
		wait_on_page_locked(page);
		kmap(page);
		if (!PageUptodate(page))
			goto fail;
		if (!PageChecked(page)) ;
		// ext2_check_page(page);
		if (PageError(page))
			goto fail;
	}
	return page;

      fail:
	lab4fs_put_page(page);
	return ERR_PTR(-EIO);
}*/

/// look in dir(file), and returns the next dir entry in (dirent)
static int lab4fs_file_readdir(struct file *file, void *dirent,
			       filldir_t filldir)
{

	// 
	struct dentry *de = file->f_dentry;
	struct inode *inode = file->f_dentry->d_inode;
	struct super_block *sb = inode->i_sb;
	int offset = 0;
	//int size=12;
	struct ext2_dir_entry_2 *dire;
	struct buffer_head *bh;
	// int i = 0;

	int blk = get_block_id(inode);

	//return 1;

	printk("readdir:  num-files = %d, size %llu\n",
	       atomic_read(&file->f_count), inode->i_size);
	printk("readdir:  number of pages = %d\n",
	       (int)(inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT);
	printk(" %s: look in dir %s, f_pos %llu\n", __func__, de->d_iname,
	       file->f_pos);

	if (file->f_pos >= 2) {
		return 1;
	}
	//static struct ext2_dir_entry_2* lab4fs_get_dir_entry( struct super_block* sb, struct inode* inode, int offset, int size, struct buffer_head *bh)

	if (filldir(dirent, ".", 1, file->f_pos++, de->d_inode->i_ino, DT_DIR))
		return 0;
	if (filldir
	    (dirent, "..", 2, file->f_pos++, de->d_parent->d_inode->i_ino,
	     DT_DIR))
		return 0;

	if (!(bh = sb_bread(sb, blk)))	//  
		//if ( !(bh= sb_bread(sb,inode->i_blocks)) )//  
	{
		printk(" %s:  unable to read superblock\n", __func__);
	}
	printk("%s: have read blk %d\n", __func__, blk);	//inode->i_blocks );

	//dire = (struct ext2_dir_entry_2*) (((char *)bh->b_data + offset) );
	//printk ("name %s, rec_len:%d, inode =%d\n",dire->name, dire->rec_len,dire->inode);
	//      printk("lab4fs_get_dir_entry after sb_bread\n");

	offset = 0;
	//for (i = 0; i < atomic_read(&file->f_count); i++ ){
	while (offset < inode->i_size) {
		printk(" %s: getting dire\n", __func__);

		dire = (struct ext2_dir_entry_2 *)(((char *)bh->b_data) + offset);	//
		//dire = lab4fs_get_dir_entry( sb, inode, offset, size);
		printk(" %s: got dire: name %s, rec_len:%d, inode =%d\n",
		       __func__, dire->name, dire->rec_len, dire->inode);

		if (dire->inode > 0)	// inode=0 means the file has been deleted
		{
			if (filldir(dirent, dire->name, dire->name_len,
				    file->f_pos++, dire->inode,
				    dire->file_type))
				return 0;
		}
		offset += dire->rec_len;
		//dire = NULL;

	}

	return 1;
}

/*
static void bfs_write_super(struct super_block *s)
 212{
 213        lock_kernel();
 214        if (!(s->s_flags & MS_RDONLY))
 215                mark_buffer_dirty(BFS_SB(s)->si_sbh);
 216        s->s_dirt = 0;
 217        unlock_kernel();
 218} */
static void lab4fs_write_super(struct super_block *sb)
{
	struct lab4fs_info *pinfo = (struct lab4fs_info *)sb->s_fs_info;

	mark_buffer_dirty(pinfo->sb_bh);	// mark the buffer of in-mem sb as dirty
	mark_buffer_dirty(pinfo->indmap_bh);
	mark_buffer_dirty(pinfo->blkmap_bh);

	sb->s_dirt = 0;
}

/*
static void bfs_put_super(struct super_block *s)
 187{
 188        struct bfs_sb_info *info = BFS_SB(s);
 189        brelse(info->si_sbh);
 190        kfree(info->si_imap);
 191        kfree(info);
 192        s->s_fs_info = NULL;
 193}	*/

static void lab4fs_put_super(struct super_block *sb)
{
	struct lab4fs_info *pinfo = (struct lab4fs_info *)sb->s_fs_info;
	brelse(pinfo->sb_bh);
	brelse(pinfo->indmap_bh);
	brelse(pinfo->blkmap_bh);

	sb->s_fs_info = NULL;
}

//

/*static struct inode *lab4fs_alloc_inode(struct super_block *sb)
{
	struct lab4fs_inode_info *indinfo;
	indinfo = kmem_cache_alloc(lab4fs_inode_cachep, SLAB_KERNEL);
	if (!indinfo) {
		printk("Error !! %s: failed to alloc ind\n", __func__);
		return NULL;
	}
	printk(" %s: get a lab4fs  inode\n", __func__);
	return &(indinfo->vfs_inode);
}*/

static int lab4fs_statfs(struct super_block *s, struct kstatfs *buf)
{
	u64 id;

//      struct lab4fs_info *info = s->s_fs_info;
	printk("%s: --------> enter \n", __func__);

	id = huge_encode_dev(s->s_bdev->bd_dev);
	buf->f_type = LAB4FS_MAGIC;
	buf->f_bsize = s->s_blocksize;
	buf->f_files = 2;
	buf->f_fsid.val[0] = (u32) id;
	buf->f_fsid.val[1] = (u32) (id >> 32);
	printk("%s: <========= exit... \n", __func__);
	return 0;

}

static struct super_operations lab4fs_sops = {
	.read_inode = lab4fs_super_read_inode,
	.statfs = lab4fs_statfs,	// simple_statfs,
//      .alloc_inode =  lab4fs_alloc_inode,
//      destory_inode: NULL,
	.delete_inode = lab4fs_super_delete_inode,	// NULL,
	.write_inode = lab4fs_super_write_inode,
	.write_super = lab4fs_write_super,
	.put_super = lab4fs_put_super,

};

void dump_inode_bmp(struct lab4fs_info *finfo)
{
	int i, cnt;
	cnt = 0;
	printk("output inode-map\n");
	for (i = 0; i < BLK_SIZE; i++) {
		printk("%c", '0' + finfo->ind_map[i]);
		if (finfo->ind_map[i] == 1)
			cnt++;
	}
	printk("\n  used inode %d, free inds %d \n", cnt,
	       finfo->super->free_inodes);
}

void dump_blk_bmp(struct lab4fs_info *info)
{
	int i, cnt;
	cnt = 0;
	printk("\noutput blk-map\n");
	for (i = 0; i < BLK_SIZE; i++) {
		printk("%c", '0' + info->blk_map[i]);
		if (info->blk_map[i] == 1)
			cnt++;
	}
	printk("\nused blks %d, free blks %d\n", cnt, info->super->free_blocks);
}

// block devices
static int lab4fs_fill_super(struct super_block *sb, void *data, int silent)
{
	//static struct inode *root_inode;
	struct buffer_head *bh, *bh1, *bh2;
	struct lab4fs_super *super;
	struct lab4fs_info *finfo;
	unsigned long sb_block = 0;	// blk-0 is super-block

	// get superblock from beginning of device
	int offset = 0;

	finfo = kmalloc(sizeof(*finfo), GFP_KERNEL);

	sb->s_fs_info = finfo;
	memset(finfo, 0, sizeof(*finfo));

	sb_set_blocksize(sb, BLK_SIZE);

	// get superblock from device: sb->s_bdev (block_device*)
	if (!(bh = sb_bread(sb, sb_block))) {
		printk("\n! Unable to read superblock");
		goto lab4fs_fill_super_failure;
	}
	super = (struct lab4fs_super *)(((char *)bh->b_data) + offset);

	finfo->sb_bh = bh;	// keeps the buffer-head at fsinfo
	finfo->super = super;	//
	memcpy(super->name, "this is test", 13);
	mark_buffer_dirty(finfo->sb_bh);
	/////////brelse(bh);    

	/// now read inode-bmp
	if (!(bh1 = sb_bread(sb, sb_block + 1))) {
		printk("lab4fs: unable to read ind-table\n");
		goto lab4fs_fill_super_failure;
	}
	finfo->ind_map = (unsigned char *)((char *)bh1->b_data);
	finfo->indmap_bh = bh1;
	dump_inode_bmp(finfo);	// dump ind-bmp

	/// read blk-map        
	if (!(bh2 = sb_bread(sb, sb_block + 2))) {
		printk("lab4fs: unable to read ind-table\n");
		goto lab4fs_fill_super_failure;
	}
	finfo->blk_map = (unsigned char *)(bh2->b_data);
	finfo->blkmap_bh = bh2;
	dump_blk_bmp(finfo);	// dump block_byte_map

	////////
	finfo->super = super;

	sb->s_magic = super->magic;	// check flags
	if (sb->s_magic != LAB4FS_MAGIC) {
		printk("LAB4fs:  b_size of bh is %d\n", bh->b_size);
		printk("LAB4fs:  sizeof super is %d\n",
		       sizeof(struct lab4fs_super));
		printk("LAB4fs:  magic we got was %ld\n", sb->s_magic);
		printk
		    ("LAB4fs:  error, can't find lab4fs superblock on dev %s.\n",
		     sb->s_id);
		goto mount_fail;
	}
	sb->s_blocksize = super->blocksize;
	sb->s_blocksize_bits = super->blocksizebits;
	sb->s_op = &lab4fs_sops;
	sb->s_type = &lab4fs;
	// debug
	//sb_set_blocksize(sb, BLK_SIZE);

	// ext2 uses 2
	//printk("lab4fs before iget...\n");

	lab4fs_root_inode = iget(sb, 1);	// get inode 1: root-inode
	printk(" %s: root inode 1\n", __func__);
	sb->s_root = d_alloc_root(lab4fs_root_inode);	// alloc dentry for root-inode
	printk(" %s: s_root dentry allocated\n", __func__);
	if (!sb->s_root) {
		printk("lab4fs: get root inode dentry failed\n");
		iput(lab4fs_root_inode);
		goto mount_fail;
	}
	//printk("lab4fs: is directory?\n");

	if (!S_ISDIR(lab4fs_root_inode->i_mode) ||
	    !lab4fs_root_inode->i_blocks || !lab4fs_root_inode->i_size) {
		if (!S_ISDIR(lab4fs_root_inode->i_mode))
			printk("lab4fs: corrupt root WRONG MODE\n");
		if (!lab4fs_root_inode->i_blocks)
			printk("lab4fs: corrupt root WRONG BLOCKS: %d\n",
			       (int)lab4fs_root_inode->i_blocks);
		if (!lab4fs_root_inode->i_size)
			printk("lab4fs: corrupt root WRONG SIZE: %d\n",
			       (int)lab4fs_root_inode->i_size);
		dput(sb->s_root);
		sb->s_root = NULL;
		goto mount_fail;
	}
	//printk("lab4fs: yes is directory?\n");

	lab4fs_root_inode->i_op = &lab4fs_iops;
	lab4fs_root_inode->i_mode = S_IFDIR | S_IRWXU;
	lab4fs_root_inode->i_fop = &lab4fs_fops;

	return 0;
      mount_fail:
	// decrement bufferhead reference count
	printk("\nMount Failure");
	brelse(bh);
      lab4fs_fill_super_failure:
	sb->s_fs_info = NULL;
	kfree(finfo);
	return -EINVAL;
}

//mount lab4fs filesystem
//
// to work with my laptop ( later kernel version )
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
static struct super_block *lab4fs_get_sb(struct file_system_type *fs_type,
					 int flags, const char *dev_name,
					 void *data)
{
	printk(" %s: devname %s\n", __func__, dev_name);
	return get_sb_bdev(fs_type, flags, dev_name, data, lab4fs_fill_super);

}
#else

static int lab4fs_get_sb(struct file_system_type *fs_type,
			 int flags, const char *dev_name, void *data,
			 struct vfsmount *mnt)
{

	return get_sb_bdev(fs_type, flags, dev_name, data, lab4fs_fill_super,
			   mnt);
}
#endif

/////////       kmem_cache_init
/*static void init_slab_lab4fs_inode(void *foo, kmem_cache_t * cachep,
				   unsigned long flags)
{
	struct lab4fs_inode_info *p = foo;
	if (p) {
		if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
		    SLAB_CTOR_CONSTRUCTOR)
			inode_init_once(&p->vfs_inode);
	} else {
		printk("\t%s: Error!!!! ind is NULL\n", __func__);
	}
}*/

/*static int init_inodecache(void)
{
	lab4fs_inode_cachep = kmem_cache_create("lab4fs_inode_cache",
						sizeof(struct
						       lab4fs_inode_info), 0,
						SLAB_RECLAIM_ACCOUNT,
						init_slab_lab4fs_inode, NULL);
	if (lab4fs_inode_cachep == NULL)
		return -ENOMEM;
	return 0;
}*/

static void destroy_inodecache(void)
{
	if (kmem_cache_destroy(lab4fs_inode_cachep))
		printk(KERN_INFO
		       "lab4fs_inode_cache: not all structures were freed\n");
	lab4fs_inode_cachep = NULL;
}

static int my_init(void)
{

	int err = 0;

	/*
	   err = init_inodecache();
	   if ( err) {
	   printk("lab4fs: my_init init_inodecache() failed\n");
	   goto out;
	   }    */

	err = register_filesystem(&lab4fs);
	if (err) {
		printk("lab4fs: my_init register_filesystem() failed\n");
		goto out2;
	}
	printk(" -- %s:  hello from my mod\n", __func__);
	memset(glb_buffer[0], 0, PAGE_SIZE * LAB4FS_PGNUM);	// clear glb_buffer
	return 0;
//out: 
	return err;
      out2:
	return err;
	destroy_inodecache();
}

static void my_exit(void)
{
	int err;
	err = unregister_filesystem(&lab4fs);
//      destroy_inodecache();
	printk(" -- %s:  exit my mod, ret %d\n", __func__, err);

}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("OY");
